package com.ztesoft.report.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ztesoft.report.base.controller.BaseController;
import com.ztesoft.report.base.pojo.R;
import com.ztesoft.report.base.service.impl.BaseServiceImpl;
import com.ztesoft.report.dao.ReportDao;
import com.ztesoft.report.dao.ServiceInterfaceDao;
import com.ztesoft.report.dao.SysDictDao;
import com.ztesoft.report.dao.SysSqlDao;
import com.ztesoft.report.engine.data.ReportDataSource;
import com.ztesoft.report.engine.data.ReportMetaDataColumn;
import com.ztesoft.report.engine.data.ReportQueryParamItem;
import com.ztesoft.report.engine.options.QueryParameterOptions;
import com.ztesoft.report.engine.options.ReportOptions;
import com.ztesoft.report.engine.query.QueryerFactory;
import com.ztesoft.report.entity.*;
import com.ztesoft.report.service.DataService;
import com.ztesoft.report.service.DatasourceService;
import com.ztesoft.report.service.ReportService;
import com.ztesoft.report.service.ReportUserRoleService;
import com.ztesoft.report.utils.AESUtil;
import com.ztesoft.report.utils.StringUtils;
import com.ztesoft.report.utils.excel.pojo.Node;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.converters.SqlDateConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * <p>
 * 报表信息表 服务实现类
 * </p>
 *
 * @author y3tu
 * @since 2018-05-29
 */
@Service
public class ReportServiceImpl extends BaseServiceImpl<ReportDao, Report> implements ReportService {

    @Autowired
    private DatasourceService datasourceService;
    @Autowired
    private DataService dataService;
    @Autowired
    private ServiceInterfaceDao serviceInterfaceDao;
    @Autowired
    private ReportDao reportDao;
    @Autowired
    private SysSqlDao sysSqlDao;
    @Autowired
    SysDictDao sysDictDao;
    @Autowired
    ReportService reportService;

    @Autowired
    private ReportUserRoleService reportUserRoleService;
    int i = 0;

    @Override
    public List<Report> getByPage(PageInfo page, String fieldName, Integer categoryId) {
        return null;
    }

    @Override
    public boolean saveQueryParams(int id, String json) {
        return false;
    }

    @Override
    public Report getByUid(String uid) {
        List<Report> reportList = this.selectList(new EntityWrapper<Report>().eq("uid", uid));
        if (reportList.size() > 0) {
            return reportList.get(0);
        } else {
            return null;
        }
    }

    @Override
    public String getSqlText(int id) {
        Report report = this.selectById(id);
        return report==null?"":report.getSqlText();
    }

    @Override
    public List<ReportMetaDataColumn> getMetaDataColumns(int dsId, String sqlText) {
        final List<ReportMetaDataColumn> metaDataColumns = this.executeSqlText(dsId, sqlText);
        return metaDataColumns;
    }

    @Override
    public void explainSqlText(int dsId, String sqlText) {

    }

    @Override
    public List<ReportMetaDataColumn> executeSqlText(int dsId, String sqlText) {
        final ReportDataSource reportDataSource = getReportDataSource(dsId);
        return QueryerFactory.create(reportDataSource).parseMetaDataColumns(sqlText);
    }

    @Override
    public List<ReportQueryParamItem> executeQueryParamSqlText(int dsId, String sqlText) {
        return null;
    }

    @Override
    public List<ReportMetaDataColumn> parseMetaColumns(String json) {
        if (org.apache.commons.lang3.StringUtils.isBlank(json)) {
            return new ArrayList<>(0);
        }
        return JSON.parseArray(json, ReportMetaDataColumn.class);
    }

    @Override
    public List<QueryParameterOptions> parseQueryParams(String json) {
        if (org.apache.commons.lang3.StringUtils.isBlank(json)) {
            return new ArrayList<>(0);
        }
        return JSON.parseArray(json, QueryParameterOptions.class);
    }

    @Override
    public ReportOptions parseOptions(String json) {
//        if (org.apache.commons.lang3.StringUtils.isBlank(json)) {
//            return ReportOptions.builder().dataRange(7).layout(1).statColumnLayout(1).build();
//        }
        return JSON.parseObject(json, ReportOptions.class);
    }

    @Override
    public ReportDataSource getReportDataSource(int dsId) {
        final Datasource ds = this.datasourceService.selectById(dsId);
        Map<String, Object> options = new HashMap<>(3);
        if (StringUtils.isNotEmpty(ds.getOptions())) {
            options = JSON.parseObject(ds.getOptions());
        }
        return new ReportDataSource(
                ds.getDriverClass(),
                ds.getDatasourceType(),
                ds.getJdbcUrl(), ds.getDatasourceUser(), AESUtil.decrypt(ds.getPassword()),
                ds.getQueryerClass(),
                ds.getPoolClass(),
                options);
    }

    @Override
    public Map addReport(ReportDto po) {
        //直接把ReportDto的值赋给Report
        Report report = new Report();
        try {
            BeanUtilsBean.getInstance().getConvertUtils()
                    .register(new SqlDateConverter(null), Date.class);
            BeanUtils.copyProperties(report,po);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        report.setId(null);
        report.setRemarks("");
        report.setSeq(0);
        reportService.insert(report);
        ReportUserRole reportUserRole=new ReportUserRole();
        reportUserRole.setReportId(report.getId()+"");
        reportUserRole.setUserId(BaseController.getCurrentUser()+",");
        reportUserRole.setCreateUser(BaseController.getCurrentUser());
        reportUserRole.setUpdateUser(BaseController.getCurrentUser());
        reportUserRoleService.insert(reportUserRole);
        return R.ok();
    }

    @Override
    public Map updateReport(ReportDto po) {
        //判断是否有 数据没有处理完成
        int count = serviceInterfaceDao.getNoPassCount(po.getId());
        if (count > 0) {
            throw new NullPointerException();
        }
        reportDao.updateReport(po);
        return R.ok();
    }

    @Override
    public Map previewReport(ReportDto po) {
        List<Map<String, Object>> header = getHeader(po.getHeader());
        //限制10个数据
        String sql = po.getSqlText();
        List<Map> list = dataService.executeQuerySql(po.getDsId().toString(), sql);
        if (list != null && list.size() > 10) {
            list = list.subList(0, 10);
        }
        this.valueParseByConvertType(po, list);
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("header", header);
        result.put("list", list);
        return R.ok(result);
    }

    /**
     * 通过转换类型值转换
     * @param po 报表对象
     * @param list 需要替换的数组集合
     */
    @Override
    public void valueParseByConvertType(ReportDto po, List<Map> list) {
        JSONArray jsonArray = JSON.parseArray(po.getMetaColumns());
        Map<String, JSONObject> map = new HashMap<String, JSONObject>();
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject obj = jsonArray.getJSONObject(i);
            map.put((String) obj.get("name"), obj);
        }
        for (Map result: list) {
            for (Object str : result.keySet()) {
                String field = str.toString();
                //oracle因为之前在List封装了分页RN,所以此字段要忽略(详细见DataServiceImpl.java的executeQuerySqlByPage方法)
                if ("RN".equals(field.toUpperCase())) {
                    continue;
                }
                int convertType = Integer.parseInt(map.get(field).get("convertType").toString());
                boolean isPercent = Boolean.parseBoolean(map.get(field).get("isPercent").toString());
                if (convertType != 1) {
                    String convertValue = map.get(field).get("convertValue").toString();
                    //字典转换
                    if (convertType == 2) {
                        SysDictItem sysDictItem = new SysDictItem();
                        sysDictItem.setDictCode(convertValue);
                        sysDictItem.setValue(result.get(str).toString());
                        String name = sysDictDao.getNameByDictCode(sysDictItem);
                        result.put(str, name);
                    }
                    //sql 转换
                    else if (convertType == 3) {
                        SysSql sysSql = new SysSql();
                        sysSql.setSqlCode(convertValue);
                        sysSql = sysSqlDao.getSelectListBySqlCode(sysSql);
                        List<Map> sqlTemp = dataService.
                                executeQuerySql(sysSql.getDatasourceId()+"", sysSql.getSqlText());
                        String name = "";
                        out:for (Map sqlObj: sqlTemp) {
                            boolean bool = false;
                            for (Object key : sqlObj.keySet()) {
                                String value = sqlObj.get(key).toString();
                                //如果值相同
                                if (result.get(str).toString().equals(value)) {
                                    bool = true;
                                    continue ;
                                }
                                name = value;
                            }
                            if (bool) {
                                break ;
                            }
                        }
                        result.put(str, name);
                    }
                }
                //如果要加百分号
                if (isPercent) {
                    String value = result.get(str).toString();
                    value += "%";
                    result.put(str, value);
                }
            }
        }
    }

    /**
     * 通过转换类型值转换
     * @param jsonArray 是否加百分号和字典转换
     * @param list 需要替换的数组集合
     */
    public void valueParseByConvertType(JSONArray jsonArray, List<Map> list) {
        Map<String, JSONObject> map = new HashMap<String, JSONObject>();
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject obj = jsonArray.getJSONObject(i);
            map.put((String) obj.get("name"), obj);
        }
        for (Map result: list) {
            for (Object str : result.keySet()) {
                String field = str.toString();
                //oracle因为之前在List封装了分页RN,所以此字段要忽略(详细见DataServiceImpl.java的executeQuerySqlByPage方法)
                if ("RN".equals(field.toUpperCase())) {
                    continue;
                }
                int convertType = Integer.parseInt(map.get(field).get("convertType").toString());
                boolean isPercent = Boolean.parseBoolean(map.get(field).get("isPercent").toString());
                if (convertType != 1) {
                    String convertValue = map.get(field).get("convertValue").toString();
                    //字典转换
                    if (convertType == 2) {
                        SysDictItem sysDictItem = new SysDictItem();
                        sysDictItem.setDictCode(convertValue);
                        sysDictItem.setValue(result.get(str).toString());
                        String name = sysDictDao.getNameByDictCode(sysDictItem);
                        result.put(str, name);
                    }
                    //sql 转换
                    else if (convertType == 3) {
                        SysSql sysSql = new SysSql();
                        sysSql.setSqlCode(convertValue);
                        sysSql = sysSqlDao.getSelectListBySqlCode(sysSql);
                        List<Map> sqlTemp = dataService.
                                executeQuerySql(sysSql.getDatasourceId()+"", sysSql.getSqlText());
                        String name = "";
                        out:for (Map sqlObj: sqlTemp) {
                            boolean bool = false;
                            for (Object key : sqlObj.keySet()) {
                                String value = sqlObj.get(key).toString();
                                //如果值相同
                                if (result.get(str).toString().equals(value)) {
                                    bool = true;
                                    continue ;
                                }
                                name = value;
                            }
                            if (bool) {
                                break ;
                            }
                        }
                        result.put(str, name);
                    }
                }
                //如果要加百分号
                if (isPercent) {
                    String value = result.get(str).toString();
                    value += "%";
                    result.put(str, value);
                }
            }
        }
    }



    /**
     * 获取表头的JSON对象
     * @param header
     * @return
     */
    public List<Map<String, Object>> getHeader(String header) {
        JSONArray array = JSON.parseArray(header);
        List<String> headConfig = new LinkedList<String>();
        for (Object obj : array) {
            String str = obj.toString();
            headConfig.add(str);
        }
        //获取多级表头配置 start
        //选用LinkedHashMap，保证迭代顺序的一致性
        Map<String, Object> headMap = new LinkedHashMap<>();
//String headStr : headConfig
        for (int i = 0 ; i< headConfig.size() ; i++) {
            String[] arr = headConfig.get(i).split("==");
            String config = arr[1];
            Map configMap = JSON.parseObject(config, Map.class);
            //这里为了避免表头重复情况，要在map的key增加一个标识符，后面在处理时候去掉
            headMap.put(arr[0] + "@a@" + String.valueOf(i), configMap);
        }
        //迭代Map集合，并重构一套“根目录”
        Node root = new Node();
        for (Map.Entry<String, Object> entry : headMap.entrySet()) {
            root.addNodeByStringExpressionAndWidth(entry.getKey(), (Map<String, Object>) entry.getValue());
        }

        return getListMap(root);
    }

    public List<Map<String, Object>> getListMap(Node root) {
        List<Map<String, Object>> list = new LinkedList<Map<String, Object>>();
        if (root.getMap().size() > 0) {
            for (Map.Entry<String, Node> entry : root.getMap().entrySet()) {
                Node node = entry.getValue();
                String label = node.getText();
                //去掉前面表头标识符，如果有的话
                if (label.contains("@a@")) {
                    label = label.substring(0,label.indexOf("@a@"));
                }
                String prop = node.getField();
                int id = ++i;
                List<Map<String, Object>> children = getListMap(node);
                Map<String, Object> tableColunm = new LinkedHashMap<String, Object>();
                tableColunm.put("label" , label);
                tableColunm.put("prop" , prop);
                tableColunm.put("id" , id);
                tableColunm.put("children" , children);
                list.add(tableColunm);
            }
        }
        return list;
    }

    @Override
    public Object queryMid(StringBuilder sql, Map<String, Object> map,int current,int size,int dsID,String header,String meta) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != null && entry.getValue() != ""){
                String value = entry.getValue().toString();
                String key = entry.getKey();//key里面包含了字段已经type，比如 A1 代表字段A 类型1
                String field = key.substring(0,key.length()-1);//字段
                String type = key.substring(key.length()-1);//类型
                switch (type) {
                    case "1":
                        sql.append(" and " + field + " like '%" + value + "%' ");
                        break;
                    case "2":
                        String[] date = handleDate(value);
                        sql.append(" and " + field + " >= to_date(" + date[0] + ",'yyyyMMdd') and " + field + " <= to_date(" + date[1] + ",'yyyyMMdd')");
                        break;
                    case "3":
                        sql.append(" and " + field + " = '" + value + "'");
                        break;
                    case "4":
                        sql.append(" and " + field + " = '" + value + "'");
                        break;
                    case "5":
                        sql.append(" and " + field + " = '" + value + "'");
                        break;
                }

            }
        }
        PageHelper.startPage(current,size);
        List<Map> midList = dataService.executeQuerySqlByPage(String.valueOf(dsID),sql.toString(),size,current);
        //下面进行加百分号和字典转换
        JSONArray jsonArray = JSON.parseArray(meta);
        this.valueParseByConvertType(jsonArray,midList);
        Integer count = dataService.executeQuerySqlCount(String.valueOf(dsID),sql.toString());
        //List<Map<String,Object>> midList = reportDao.selectMidList(sql.toString());
        Map resultMap = new HashMap();
        resultMap.put("total",count);
        resultMap.put("pageNum",current);
        resultMap.put("header",getHeader(header));
        resultMap.put("list",midList);
/*        PageInfo<Map> page = new PageInfo(midList);
        page.setTotal(count);
        page.setPageNum(current);
        page.setPageSize(size);*/
        return R.ok(resultMap);
    }

    @Override
    public R searchMidBill(Map map) {
        String midSql = map.get("tableSql").toString();
        //获取中间表的账期字段最大值和甘肃约定是bill_month名字，所有这个字段固定为bill_month5
        midSql = midSql.toString().replace("*","max(BILL_MONTH)");
        List<Map> midData = dataService.executeQuerySql(map.get("dsiD").toString(),midSql);
        String maxBill = "";
        if (midData.get(0) != null) {
            maxBill = midData.get(0).get("MAX(BILL_MONTH)").toString();
        }
        return R.ok(maxBill);
    }

    /**
     * 取出开始日期和结束日期，以String数组形式返回
     * @param dateBefore
     * @return
     */
    public String[] handleDate(String dateBefore){
        dateBefore = dateBefore.replace("[","");
        dateBefore = dateBefore.replace("]","");
        return dateBefore.split(",");
    }
}
